#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include "server.hpp"
#include "network.hpp"
#include "string-lib.hpp"

vector<User*> clientList;
vector<Table*> tableList;

inline list<User*>::iterator find(list<User*> &userList, User* user)
{
    list<User*>::iterator p;
    for (p = userList.begin(); p != userList.end(); p++)
    {
        if (*p == user)
        {
            return p;
        }
    }
    return userList.end();
}

int User::parse(string line)
{
    buffer.append(line);
    if (buffer.find_first_of("\0x17") == buffer.npos)
    {
        if (buffer.size() > 255)
            return -1;
        return 0;
    }
    line = buffer.substr(0, buffer.find_first_of("\0x17"));
    buffer = buffer.substr(buffer.find_first_of("\0x17") + 1);
    vector<string> tokens;
    stringlib::tokenise(line, tokens);
    if (tokens.size() == 0)
    {
        return 0;
    }

    if (arg::check(tokens, "say", 2))
    {
        string message;
        if (arg::final(tokens, message) && table != NULL)
            table->sendall(message);
        return 0;
    }

    if (arg::check(tokens, "join", 2))
    {
        bool spectator = arg::option1(tokens, "-s");
        string tableString;
        if (arg::final(tokens, tableString))
        {
            // Stringstreams supports much more formats, but is slower than atoi.
            int tableNumber = atoi(tableString.c_str());
            if (tableNumber >= 0 && (unsigned int)tableNumber < tableList.size())
            {
                if (table != NULL)
                    table->removeUser(this);
                table = tableList[tableNumber];
                table->addUser(this, spectator);
            }
        }
        return 0;
    }

    if (arg::check(tokens, "leave", 1))
    {
        return 1;
    }

    return 0;
}

User* getUserByFD(list<User*> &userList, int fd)
{
    list<User*>::iterator p;
    for (p = userList.begin(); p != userList.end(); p++)
    {
        if ((*p)->socketfd == fd)
        {
            return (*p);
        }
    }
    return NULL;
}

void Table::removeUser(User* user)
{
    list<User*>::iterator p = find(userList, user);
    if (p != userList.end())
    {
        (*p)->table = NULL;
        userList.erase(p);
        if ((*p == white || *p == black) && playing)
        {
            resign(user);
        }
    }
}

void Table::addUser(User* user, bool player)
{
    list<User*>::iterator p = find(userList, user);
    if (p == userList.end())
    {
        if (player && white == NULL)
        {
            white = user;
        }
        else if (player && black == NULL)
        {
            black = user;
        }
        user->table = this;
        userList.push_back(user);

        if (white != NULL && black != NULL)
        {
            startGame();
        }
    }
}

void Table::startGame()
{
    playing = true;
    delete board;
    board = new Board;
}

void Table::resign(User* user)
{
    playing = false;
}

void Table::draw()
{
    playing = false;
}

void Table::move(Coord src, Coord dest, User* user)
{
    if (playing && ((board->side == PS_WHITE && user == white) || (board->side == PS_BLACK && user == black)))
    {
        if (board->chk(src, dest, false, user->promotion));
        {
            // Send all the users the new board layout.
        }
    }
}

void Table::sendall(string message)
{
    list<User*>::iterator p;
    for (p = userList.begin(); p != userList.end(); p++)
    {
        send("usrmsg \"" + message + "\"", (*p)->socketfd);
    }
}

int startServer(unsigned int tableCount, unsigned int userLimit)
{
    int serverfd;
    SocketSet set;
    char buf[256];

    for (unsigned int i = 0; i < tableCount; i++)
    {
        Table* newTable = new Table;
        tableList.push_back(newTable);
    }

    serverfd = start((char *)"35780");
    if (serverfd < 0)
        return -1;

    set.add(serverfd);
    while (true)
    {
        set.scan();
        if (set.check(serverfd)) // Incoming connection
        {
            if (clientList.size() < userLimit)
            {
                User* newUser = new User();
                newUser->socketfd = accept(serverfd);
                newUser->table = NULL;

                clientList.push_back(newUser);
                set.add(newUser->socketfd);
                cout << "New user connected with id " << newUser->socketfd << endl;
                continue;
            }
            else
            {
                int socketfd = accept(serverfd);
                send("disconnect \"Server Full\"", socketfd);
                close(socketfd);
            }
        }
        vector<User*>::iterator p;
        for (p = clientList.begin(); p < clientList.end(); p++)
        {
            if (set.check((*p)->socketfd))
            {
                if (recieve(buf, 256, (*p)->socketfd) <= 0)
                {
                    cout << "User " << (*p)->socketfd << " disconnected with code 0 (connection closed)" << endl;
                    set.remove((*p)->socketfd);
                    close((*p)->socketfd);
                    delete (*p);
                    clientList.erase(p);
                }
                else
                {
                    int result = 0;// Handle the stuff here
                    result = (*p)->parse(buf);
                    if (result != 0)
                    {
                        cout << "User " << (*p)->socketfd << " disconnected with code " << (*p)->socketfd << endl;
                        set.remove((*p)->socketfd);
                        close((*p)->socketfd);
                        delete (*p);
                        clientList.erase(p);
                    }
                }
            }
        }
    }
    for (vector<User*>::iterator p = clientList.begin(); p != clientList.end(); p++)
    {
        close((*p)->socketfd);
        delete (*p);
    }
    for (vector<Table*>::iterator p = tableList.begin(); p != tableList.end(); p++)
    {
        delete (*p);
    }
    close(serverfd);
    return 0;
}

int main()
{
    return startServer(20, 30);
}

